import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.TreeSet;

public final class NormalStrategy extends Strategy {

	public NormalStrategy(MyBot bot) {
		super(bot);
	}

	@Override
	public final void execute() {

		Ants ants = mBot.getAnts();
		mFreeAnts.clear();
		mFreeAnts.addAll(ants.getMyAnts());

		MyBot.debug.logPerformanceStart("Initial");
		this.updateEnemyHillsLocation(ants);
		this.updateVisibleArea();
		this.updateExplorationMap();
		this.preventSteppingOwnHill();
		MyBot.debug.logPerformanceEnd("Initial");
		MyBot.debug.logPerformanceStart("Initial2");
		this.updateEnemyHillDiff();
		MyBot.debug.logPerformanceEnd("Initial2");
		// this.unblockOwnHill();

		{
			// DEBUG purpose:
			// Route route = getRoute(new Tile(20, 15), new Tile(0, 15));
			// MyBot.debug.log(Debug.DEBUG_ROUTE, String.format("%s", route));
		}

		{
			attackHillTask.clear();
		}

		attackHillsDetermine(mFreeAnts);
		updateIgnoreList();
		findFood(mFreeAnts);

		MyBot.debug.logPerformanceStart(Debug.DEBUG_EXPLORE);
		{
			Iterator<Entry<Tile, Task>> itr = exploreTask.entrySet().iterator();
			int i = 0;
			while (itr.hasNext()) {
				Entry<Tile, Task> entry = itr.next();
				Task task = entry.getValue();
				if (!mFreeAnts.contains(task.source)) {
					itr.remove();
					MyBot.debug.log(Debug.DEBUG_EXPLORE_INIT,
							String.format("%s, Lost ants", task.source));
				} else if (task.source.equals(task.target)) {
					itr.remove();
					MyBot.debug.log(Debug.DEBUG_EXPLORE_INIT, String.format(
							"%s, Destination reach.", task.source));
				} else if (!ants.getIlk(task.target).isPassable()) {
					MyBot.debug.log(Debug.DEBUG_EXPLORE_INIT, String.format(
							"Remove not passable explore. %s", task.target));
					itr.remove();
				}

				i++;
			}
		}
		MyBot.debug.logPerformanceEnd(Debug.DEBUG_EXPLORE);

		exploreMap(mFreeAnts, false);
		attackHills(mFreeAnts);
		exploreMap(mFreeAnts, true);
		executeTasks();
		dummyMovement(mFreeAnts);

		// Debug Nothing to do ants.
		{
			// TreeSet<Tile> sortedAnts = new TreeSet<Tile>(ants.getMyAnts());
			// explore unseen areas
			// for (Tile antLoc : sortedAnts) {
			for (Iterator<Tile> itr = mFreeAnts.iterator(); itr.hasNext();) {
				final Tile antLoc = itr.next();
				if (!mBot.orders.containsValue(antLoc)) {
					MyBot.debug.log(Debug.DEBUG_EXECUTE,
							String.format("Nothing TODO: %s", antLoc));
					itr.remove();
				}
			}
		}
	}

	private void dummyMovement(final TreeSet<Tile> freeAnts) {

		{
			// explore unseen areas
			for (Iterator<Tile> itr = freeAnts.iterator(); itr.hasNext();) {
				final Tile antLoc = itr.next();
				if (!mBot.orders.containsValue(antLoc)) {
					for (Aim direction : Aim.values()) {
						if (doMoveDirection(antLoc, direction)) {
							MyBot.debug.log(Debug.DEBUG_EXECUTE, String.format(
									"Dummy: %s -> %s", antLoc, direction));
							itr.remove();
							break;
						}
					}
				}
			}
		}
	}

	protected final void updateIgnoreList() {
		Iterator<Entry<Tile, IgnoreItem>> itr = this.ignoreList.entrySet()
				.iterator();

		while (itr.hasNext()) {
			Entry<Tile, IgnoreItem> entry = itr.next();
			IgnoreItem item = entry.getValue();

			item.updateTurn();
			if (item.ignoreTimeout <= 0) {
				itr.remove();
			}
		}
	}

	protected final void findFood(TreeSet<Tile> freeAnts) {

		long foodStartTime = System.currentTimeMillis();
		Ants ants = mBot.getAnts();

		MyBot.debug.logPerformanceStart(Debug.DEBUG_FOOD);
		{
			// Remove those food that is not longer available.
			Iterator<Task> itr = foodTask.iterator();
			while (itr.hasNext()) {
				Task task = itr.next();
				Tile t = task.target;
				if (ants.isVisible(t) && ants.getIlk(t) != Ilk.FOOD) {
					itr.remove();
				} else {
					// Remove all interested ants.
					// This will be updated later.
					task.reset();
				}
			}
		}

		{
			// Add any new food spawn.
			TreeSet<Tile> sortedFood = new TreeSet<Tile>(ants.getFoodTiles());
			for (Tile foodLoc : sortedFood) {
				if (!ignoreList.containsKey(foodLoc)) {
					Task task = new Task(Task.TaskType.FOOD, foodLoc);
					if (!foodTask.contains(task)) {
						foodTask.add(task);
					}
				}
			}
		}

		{
			// ArrayList<Tile> myAnts = new ArrayList<Tile>(ants.getMyAnts());
			Iterator<Task> itr = foodTask.iterator();
			ArrayList<Route> heuristicRoute = new ArrayList<Route>();
			while (itr.hasNext()) {
				Task task = itr.next();

				Tile source;
				Tile target = task.getTarget();
				Route oldRoute;
				boolean replace = false;
				int routeCost;

				heuristicRoute.clear();

				for (Iterator<Tile> antsItr = freeAnts.iterator(); antsItr
						.hasNext();) {
					source = antsItr.next();
					Route route = new Route(source, target,
							Route.RouteType.HEURISTIC, null,
							this.getHeuristicDistance(source, target));
					heuristicRoute.add(route);
					MyBot.debug.log(Debug.DEBUG_FOOD, "Heuristic: " + route);
				}

				Collections.sort(heuristicRoute);

				MyBot.debug.log(Debug.DEBUG_FOOD,
						"==============================");
				for (int i = 0; i < heuristicRoute.size(); i++) {
					replace = false;
					oldRoute = task.route;
					source = heuristicRoute.get(i).getStart();

					MyBot.debug.log(Debug.DEBUG_FOOD, String.format(
							"Source: %s, Target: %s", source, target));
					Route newRoute = getRouteIfExist(source, target);

					if (newRoute == null) {
						routeCost = this.getHeuristicDistance(source, target);
					} else {
						routeCost = newRoute.getDistance();
					}

					if (oldRoute == null) {
						long startTime = System.currentTimeMillis();
						newRoute = getRoute(source, target);
						long endTime = System.currentTimeMillis();
						if (endTime - startTime > MyBot.ROUTE_TIMEOUT) {
							MyBot.debug.log(Debug.DEBUG_PERFORMANCE,
									(endTime - startTime) + " Ignore: "
											+ target);
							this.ignoreList.put(target, new IgnoreItem(target));
							itr.remove();
							break;
						}
						if (newRoute != null) {
							MyBot.debug.log(
									Debug.DEBUG_FOOD,
									String.format("New route: %s",
											newRoute.toString()));
							replace = true;
						}
					} else {
						if (oldRoute.getDistance() > routeCost) {
							// Create the new route if not available.
							if (newRoute == null) {
								newRoute = getRoute(
										source,
										target,
										new LessThanCondition(oldRoute
												.getDistance()));
							}
							if (newRoute != null) {
								MyBot.debug
										.log(Debug.DEBUG_FOOD,
												String.format(
														"New better heuristic. %d < %d",
														oldRoute.getDistance(),
														routeCost));
								replace = true;
							} else {
								MyBot.debug
										.log(Debug.DEBUG_FOOD,
												String.format(
														"Old better heuristic. %d, not continue.",
														oldRoute.getDistance()));
							}
						} else {
							// Old route is better.
							MyBot.debug.log(Debug.DEBUG_FOOD, String.format(
									"Old better. %d < %d",
									oldRoute.getDistance(), routeCost));
						}
					}

					if (replace) {
						task.source = newRoute.getStart();
						task.route = newRoute;
					}
				}
				MyBot.debug.log(Debug.DEBUG_FOOD,
						"==============================");
				
				final long foodEndTime = System.currentTimeMillis();
				if (foodEndTime - foodStartTime > FOOD_TASK_TIMEOUT) {
					// The time limit for food exploration reach.
					MyBot.debug.log(Debug.DEBUG_PERFORMANCE, "Food task time out.");
					break;
				}
			}
		}

		{
			Collections.sort(foodTask);
			for (Iterator<Task> itr = foodTask.iterator(); itr.hasNext();) {

				Task task = itr.next();
				if (freeAnts.contains(task.source)) {
					MyBot.debug.log(Debug.DEBUG_EXECUTE_PRE,
							task.source.toString());
					freeAnts.remove(task.source);
				} else {
					itr.remove();
					// MyBot.debug.log(Debug.DEBUG_EXECUTE, task.source
					// + " - Duplicate food task for same ant. ");
				}
			}

		}

		// {
		// Collections.sort(foodTask);
		// Map<Tile, Tile> foodTargets = new HashMap<Tile, Tile>();
		// for (Iterator<Task> itr = foodTask.iterator(); itr.hasNext();) {
		// Task task = itr.next();
		// Route route = task.route;
		// // if (!foodTargets.containsKey(route.getEnd())
		// // && !foodTargets.containsValue(route.getStart())
		// if (!foodTargets.containsKey(route.getEnd())
		// && !foodTargets.containsValue(route.getStart())
		// && doMoveDirection(route.getStart(), route.getNext())) {
		// MyBot.debug.log(
		// Debug.DEBUG_EXECUTE,
		// "Food - " + route.getStart() + ", "
		// + route.getEnd() + ", " + route.getNext());
		// foodTargets.put(route.getEnd(), route.getStart());
		// }
		// }
		// }
		MyBot.debug.logPerformanceEnd(Debug.DEBUG_FOOD);
	}

	protected final void attackHillsDetermine(TreeSet<Tile> freeAnts) {

		MyBot.debug.logPerformanceStart(Debug.DEBUG_ATTACK);

		Iterator<Tile> itr = this.enemyHills.iterator();

		// TODO: Limit time.
		final long attackStartTime = System.currentTimeMillis();
		
		while (itr.hasNext()) {
			final Tile enemyHill = itr.next();
			for (Iterator<Tile> antsItr = freeAnts.iterator(); antsItr
					.hasNext();) {
				final Tile antLoc = antsItr.next();

				if (this.getHeuristicDistance(enemyHill, antLoc) < DETERMINE_ATTACK_DISTANCE) {
					Route route = getRoute(antLoc, enemyHill);
					if (route != null) {
						Task task = new Task(Task.TaskType.ATTACK, enemyHill);
						task.route = route;
						task.source = antLoc;
						attackHillTask.put(antLoc, task);
						antsItr.remove();
					}
				}
			}
			
			final long attackEndTime = System.currentTimeMillis();
			if (attackEndTime - attackStartTime > FOOD_TASK_TIMEOUT) {
				// The time limit for food exploration reach.
				MyBot.debug.log(Debug.DEBUG_PERFORMANCE, "Attack task time out.");
				break;
			}
		}
		MyBot.debug.logPerformanceEnd(Debug.DEBUG_ATTACK);
	}

	protected final void attackHills(TreeSet<Tile> freeAnts) {

		MyBot.debug.logPerformanceStart(Debug.DEBUG_ATTACK);

		{
			// Perform attack hills.
			// explore unseen areas
			for (Iterator<Tile> antsItr = freeAnts.iterator(); antsItr
					.hasNext();) {
				final Tile antLoc = antsItr.next();
				Tile target = getAttackHill(antLoc);
				if (target != null) {
					Task task = new Task(Task.TaskType.ATTACK, target);
					task.source = antLoc;
					attackHillTask.put(antLoc, task);
					antsItr.remove();
					// //TODO: Remove the free ants
					// // Add them back if the location cannot move.
					// if (doMoveLocation(antLoc, target)) {
					MyBot.debug.log(Debug.DEBUG_EXECUTE_PRE,
							String.format("Attack: %s -> %s", antLoc, target));
					// }
				}
			}
		}
		MyBot.debug.logPerformanceEnd(Debug.DEBUG_ATTACK);
	}

	protected void exploreMap(TreeSet<Tile> freeAnts, boolean flag) {

		// Ants ants = mBot.getAnts();
		MyBot.debug.logPerformanceStart(Debug.DEBUG_EXPLORE);
		{
			// Perform exploration.
			// explore unseen areas
			long exploreStartTime = System.currentTimeMillis();

			MyBot.debug.log(Debug.DEBUG_EXECUTE_PRE,
					"Free ants: " + freeAnts.size());
			for (Iterator<Tile> itr = freeAnts.iterator(); itr.hasNext();) {
				final Tile antLoc = itr.next();
				MyBot.debug
						.log(Debug.DEBUG_EXECUTE_PRE, "Free ants: " + antLoc);
				Tile target;
				Task task;

				if (!exploreTask.containsKey(antLoc)) {
					target = getNearestExplorationArea(antLoc, flag);

					if (target != null && !ignoreList.containsKey(target)) {
						task = new Task(Task.TaskType.EXPLORE, target);
						task.source = antLoc;
						long startTime = System.currentTimeMillis();
						Route route = getRoute(antLoc, target);
						long endTime = System.currentTimeMillis();
						if (endTime - startTime > MyBot.ROUTE_TIMEOUT) {
							this.ignoreList.put(target, new IgnoreItem(target));
						}
						if (route != null) {
							task.route = route;
							exploreTask.put(antLoc, task);
							itr.remove();
						}
						MyBot.debug.log(Debug.DEBUG_EXPLORE, String.format(
								"NEW: %s: %s", antLoc, task.route));
					} else {
						MyBot.debug.log(Debug.DEBUG_EXPLORE, String.format(
								"NEW: %s->%s: null", antLoc, target));
					}
				} else {
					task = exploreTask.get(antLoc);
					task.source = antLoc;
					target = task.target;
					long startTime = System.currentTimeMillis();
					task.route = getRoute(antLoc, target);
					long endTime = System.currentTimeMillis();
					if (endTime - startTime > MyBot.ROUTE_TIMEOUT) {
						this.ignoreList.put(target, new IgnoreItem(target));
						exploreTask.remove(antLoc);
					} else {
						itr.remove();
					}
					// Continue the explore task
					MyBot.debug.log(Debug.DEBUG_EXPLORE,
							String.format("CON: %s: %s", antLoc, task.route));
				}
				
				final long exploreEndTime = System.currentTimeMillis();
				if (exploreEndTime - exploreStartTime > FOOD_TASK_TIMEOUT) {
					// The time limit for food exploration reach.
					MyBot.debug.log(Debug.DEBUG_PERFORMANCE, "Explore task time out.");
					break;
				}
			}
		}

		// {
		// Iterator<Entry<Tile, Task>> itr = exploreTask.entrySet().iterator();
		// while (itr.hasNext()) {
		//
		// Entry<Tile, Task> entry = itr.next();
		// Task task = entry.getValue();
		// if (freeAnts.contains(task.source)) {
		// MyBot.debug.log(Debug.DEBUG_EXECUTE_PRE,
		// task.source.toString());
		// freeAnts.remove(task.source);
		// } else {
		// MyBot.debug.log(Debug.DEBUG_EXECUTE_PRE, task.source
		// + " - Duplicate explore task. ");
		// }
		// }
		//
		// }

		// {
		// // Execute exploration.
		// Iterator<Entry<Tile, Task>> itr = exploreTask.entrySet().iterator();
		// HashMap<Tile, Task> newExploreTask = new HashMap<Tile, Task>();
		// while (itr.hasNext()) {
		// Entry<Tile, Task> entry = itr.next();
		// Task task = entry.getValue();
		// Route route = task.route;
		//
		// if (doMoveDirection(route.getStart(), route.getNext())) {
		// MyBot.debug.log(
		// Debug.DEBUG_EXECUTE,
		// "Explore - "
		// + route
		// + ", Next: "
		// + ants.getTile(route.getStart(),
		// route.getNext()));
		// task.source = ants.getTile(route.getStart(),
		// route.getNext());
		// if (!task.source.equals(task.target)) {
		// newExploreTask.put(task.source, task);
		// } else {
		// itr.remove();
		// MyBot.debug.log(Debug.DEBUG_EXECUTE,
		// "Destination reach.1");
		// }
		//
		// } else {
		// // itr.remove();
		// // Task cannot perform/ changed.
		// MyBot.debug.log(Debug.DEBUG_EXECUTE, "Explore(Remove) - "
		// + route);
		// }
		// }
		// exploreTask = newExploreTask;
		// }
		MyBot.debug.logPerformanceEnd(Debug.DEBUG_EXPLORE);
	}

	protected void executeTasks() {

		Ants ants = mBot.getAnts();

		{
			// Map<Tile, Tile> foodTargets = new HashMap<Tile, Tile>();
			for (Iterator<Task> itr = foodTask.iterator(); itr.hasNext();) {
				Task task = itr.next();
				Route route = task.route;
				// if (!foodTargets.containsKey(route.getEnd())
				// && !foodTargets.containsValue(route.getStart())
				if (doMoveDirection(route.getStart(), route.getNext())) {
					MyBot.debug.log(
							Debug.DEBUG_EXECUTE,
							"Food - " + route.getStart() + ", "
									+ route.getEnd() + ", " + route.getNext());
					// foodTargets.put(route.getEnd(), route.getStart());
				}
			}
		}

		{
			Iterator<Entry<Tile, Task>> itr = attackHillTask.entrySet()
					.iterator();
			while (itr.hasNext()) {
				Entry<Tile, Task> entry = itr.next();
				Task task = entry.getValue();

				if (!mBot.orders.containsValue(task.target)) {
					if (task.route != null) {
						Route route = task.route;
						if (doMoveDirection(task.source, route.getNext())) {
							MyBot.debug.log(Debug.DEBUG_EXECUTE, "Attack - "
									+ route.getStart() + ", " + route.getEnd()
									+ ", " + route.getNext());
							itr.remove();
						} else {
							itr.remove();
							mFreeAnts.add(task.source);
						}
					} else {
						if (doMoveLocation(task.source, task.target)) {
							MyBot.debug.log(Debug.DEBUG_EXECUTE, "Attack - "
									+ task.source + ", " + task.target);
							itr.remove();
						} else {
							itr.remove();
							mFreeAnts.add(task.source);
						}
					}
				} else {
					itr.remove();
				}
			}
		}

		{
			// Execute exploration.
			Iterator<Entry<Tile, Task>> itr = exploreTask.entrySet().iterator();
			HashMap<Tile, Task> newExploreTask = new HashMap<Tile, Task>();
			while (itr.hasNext()) {
				Entry<Tile, Task> entry = itr.next();
				Task task = entry.getValue();
				Route route = task.route;

				if (doMoveDirection(route.getStart(), route.getNext())) {
					MyBot.debug.log(
							Debug.DEBUG_EXECUTE,
							"Explore - "
									+ route
									+ ", Next: "
									+ ants.getTile(route.getStart(),
											route.getNext()));
					task.source = ants.getTile(route.getStart(),
							route.getNext());
					if (!task.source.equals(task.target)) {
						newExploreTask.put(task.source, task);
					} else {
						itr.remove();
						MyBot.debug.log(Debug.DEBUG_EXECUTE,
								"Destination reach.1");
					}

				} else {
					// itr.remove();
					// Task cannot perform/ changed.
					MyBot.debug.log(Debug.DEBUG_EXECUTE, "Explore(Remove) - "
							+ route);
				}
			}
			exploreTask = newExploreTask;
		}
	}
}
